package com.wbcs.jbsf.auth.interceptor;

import java.io.IOException;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;

import com.wbcs.config.component.IComponentConfigBean;
import com.wbcs.config.component.application.report.ReportBean;
import com.wbcs.config.component.container.AbsContainerConfigBean;
import com.wbcs.config.component.container.page.PageBean;
import com.wbcs.config.component.other.ButtonsBean;
import com.wbcs.jbsf.bean.Role;
import com.wbcs.jbsf.dao.IDataModal;
import com.wbcs.jbsf.dao.JBSFDaoTemplate;
import com.wbcs.jbsf.util.Consts;
import com.wbcs.jbsf.util.JacksonMapper;
import com.wbcs.jbsf.util.Wbcs4JBSFUtil;
import com.wbcs.system.ReportRequest;
import com.wbcs.system.buttons.AbsButtonType;
import com.wbcs.system.buttons.AbsEditableReportButton;
import com.wbcs.system.buttons.WbcsButton;
import com.wbcs.system.intercept.AbsPageInterceptor;

/**
 * wbcs用于权限过滤的通用页面拦截器类
 * @author hawkfly
 */
public class ItprAuthority extends AbsPageInterceptor
{
    private static Log log = LogFactory.getLog(ItprAuthority.class);
    private Map<String, Map<String, Object>> mauthorities;//权限列表集合
    private Map<String, List<Map<String, Object>>> mpagemenus;//页面可操作菜单列表
    private JBSFDaoTemplate jdaoTemplate;
    private Map<String, String> mrealRptAccess;
    
    @Override
	public void doStart(ReportRequest rrequest)
    {
        log.info("start ItprAuthority...");
        //根据当前用户编号和当前页面PAGEID，在权限列表中获取本页面访问权限，如无访问权限则return
        Object usercodeObj = rrequest.getRequest().getSession().getAttribute(Consts.SESSION_USERCODE);
        Object useridObj = rrequest.getRequest().getSession().getAttribute(Consts.SESSION_USERID);
        Object rolecodeObj = rrequest.getRequest().getSession().getAttribute(Consts.SESSION_ROLE);
        if(usercodeObj == null || rolecodeObj == null)rrequest.getWResponse().getMessageCollector().error("会话中不存在用户或角色信息！","", true);
        String usercode = String.valueOf(usercodeObj);
        String userid = String.valueOf(useridObj);
        @SuppressWarnings("unchecked")
        List<Role> roles = (List<Role>)rolecodeObj;
        List<String> rolecodes = Wbcs4JBSFUtil.roles2lstrolecodes(roles, false);//排除管理员角色
        
        String pageid = rrequest.getPagebean().getId();
        //List<ReportBean> lstallReportBeans = rrequest.getPagebean().getLstAllReportBeans(false);
        initAuthority(userid, pageid, rrequest.getConnection());
        Map<String, Object> pageauth = mauthorities.get(pageid);
        if(pageauth == null){//对本页面无访问权限
            rrequest.getWResponse().getMessageCollector().error("此用户对本页面无访问权限！","", true);
        }
        
        if(isRead(usercode, pageid, pageauth)){
            try {
				initPageMenus(rrequest.getConnection(), pageid, rolecodes);
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        //根据用户编号及页面编号取出本页面的访问权限（读权限，写权限，数据访问权限等）设置这些权限
            doPageAuthority(rrequest);
        //进一步获取页面的可操作菜单列表，根据菜单分类存入不同集合将这些菜单集合压入rrequest中
            packetPageMenus(rrequest);
        }else{
            rrequest.getWResponse().getMessageCollector().error("无权限访问本页面","", true);
        }
     }
    
    void initAuthority(String usercode, String pageid, Connection conn){
        if(jdaoTemplate == null)jdaoTemplate = new JBSFDaoTemplate(false);
        StringBuffer sql = new StringBuffer()
        .append("SELECT ja.*, jra.accesstype FROM jbsf_user_roles jru, jbsf_roles_authes jra, jbsf_authorize ja ")
	    .append("WHERE jru.roleid = jra.roleid and jra.authid = ja.id and jru.userid = ? and ja.code = ? ")
	    .append("union ")
	    .append("SELECT ea.*,r.accesstype FROM")
	    .append("(Select a.* From jbsf_authorize a where pid like (select id from jbsf_authorize where code = ?)) ea ")
	    .append("left join (SELECT authid, accesstype FROM jbsf_roles_authes where roleid in(SELECT roleid FROM jbsf_user_roles where userid = ?)) r ")
	    .append("on ea.id = r.authid");
        try {
			mauthorities = jdaoTemplate.<Map<String, Map<String, Object>>>select(sql.toString(),new IDataModal<Map<String, Map<String, Object>>>(){
			    public Map<String,Map<String,Object>> loadDatas(ResultSet rs, int ccount, ResultSetMetaData rmd, Map<String,Map<String,Object>> dataModal) throws SQLException
			    {
			        dataModal = new LinkedHashMap<String,Map<String,Object>>();
			        while(rs.next()){
			            Map<String, Object> rowMap = new HashMap<String, Object>();
			            for(int i=1;i<ccount+1;i++)
			            {
			                rowMap.put(rmd.getColumnName(i).toUpperCase(),rs.getObject(i));
			            }
			            String authcode = rs.getString("CODE");
			            if(dataModal.containsKey(authcode)){//取其大
			                String newAccessType = String.valueOf(rowMap.get("ACCESSTYPE"));
			                String oldAccessType = String.valueOf(dataModal.get(authcode).get("ACCESSTYPE"));
			                if(AccessTypeConst.valueOf(newAccessType).compareTo(AccessTypeConst.valueOf(oldAccessType)) > 0){
			                    dataModal.remove(authcode);
			                    dataModal.put(authcode,rowMap);
			                }
			            }else{//直接放入
			                dataModal.put(authcode,rowMap);
			            }
			            
			        }
			        return dataModal;
			    }   
			},usercode, pageid, pageid, usercode);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
    
    void initPageMenus(Connection conn, String pageid, List<?> roleids) throws SQLException{
        if(jdaoTemplate == null)jdaoTemplate = new JBSFDaoTemplate(false);
        StringBuffer sql = new StringBuffer()
        .append("SELECT m.*,jpm.pageid,jpm.roleid,jpm.icon,jpm.locationcode,lt.name locationame, lt.type packetype ")
        .append("FROM jbsf_authorize m ,jbsf_locationtype lt, jbsf_page_menus jpm ")
        .append("WHERE jpm.locationcode = lt.code and m.id = jpm.menuID and jpm.pageid = ? and jpm.roleid in(")
        .append(Wbcs4JBSFUtil.lst2dotaskStr(roleids.size())).append(")");
        mpagemenus = jdaoTemplate.<Map<String, List<Map<String, Object>>>>select(sql.toString(),new IDataModal<Map<String, List<Map<String, Object>>>>()
        {
            public Map<String,List<Map<String,Object>>> loadDatas(ResultSet rs, int ccount, ResultSetMetaData rmd, Map<String,List<Map<String,Object>>> dataModal) throws SQLException
            {
                if(mpagemenus == null)
                    dataModal = new HashMap<String,List<Map<String,Object>>>();
                else
                    dataModal = mpagemenus;
                
                while(rs.next()){
                    Map<String, Object> rowMap = new HashMap<String, Object>();
                    for(int i=0;i<ccount;i++)
                    {
                        rowMap.put(rmd.getColumnName(i),rs.getObject(i));
                    }
                    String locationcode = rs.getString("LOCATIONCODE");
                    if(dataModal.get(locationcode) == null){
                        List<Map<String, Object>> pagemenus = new ArrayList<Map<String, Object>>();
                        pagemenus.add(rowMap);
                        dataModal.put(locationcode,pagemenus);
                    }else
                        dataModal.get(locationcode).add(rowMap);
                }
                return dataModal;
            }
        },pageid, roleids);
    }
    
    boolean isRead(String usercode, String pageid, Map<String, Object> pageauth){
        //if(mauthorities == null)initAuthority(usercode, pageid, null);
        boolean b = false;
        Object displayObj = pageauth.get("ACCESSTYPE") == null ? pageauth.get("accesstype") : pageauth.get("ACCESSTYPE");
        if(displayObj == null)log.warn(new StringBuffer("页面").append(pageid).append("的可显示属性为null!"));
        else{
           String accesstype = String.valueOf(displayObj);
           if(AccessTypeConst.readonly.toString().equals(accesstype)||AccessTypeConst.write.toString().equals(accesstype)||AccessTypeConst.readwrite.toString().equals(accesstype))
               b = true;
        }
        return b;
    }
    
    void doPageAuthority(ReportRequest rrequest){
        PageBean pb = rrequest.getPagebean();
        //给页面授权
        Map<String, Object> pageauthority = mauthorities.get(pb.getId());
        if(pageauthority == null){doDefaultSet(rrequest,null,pb); return;}
        Object atObj = pageauthority.get("ACCESSTYPE") == null ? pageauthority.get("accesstype") : pageauthority.get("ACCESSTYPE");
        if(atObj == null){
            StringBuffer sb = new StringBuffer("页面").append(pb.getId()).append("的访问权限未设定!");
            doDefaultSet(rrequest,sb.toString(),pb);
            return;
        }
        exeWbcsAuthority(rrequest, String.valueOf(atObj), pb);
        //给页面的其他容器及report授权
        doAuthority(rrequest, pb.getMChildren());
    }
    
    /**
     * 组件对应的权限有存在不存在两种情况，不存在的情况下系统认为漏配权限则将其设置为不可操作！
     * 对于父组件为不可见的情况，子组件不管何种状态也是不可见的，故递归将不再向下进行扫描子组件
     * @param map
     */
    void doAuthority(ReportRequest rrequest,Map<String, IComponentConfigBean> map){
        for(String key : map.keySet()){
            IComponentConfigBean cb = map.get(key);
            Map<String, Object> rptAuthority = mauthorities.get(cb.getId());
            Object atObj = null;
            if(rptAuthority == null){//子组件未在权限配置中设定
                Map<String, Object> parentAuth = getValidComponent(cb);
                atObj = parentAuth.get("ACCESSTYPE") == null ? parentAuth.get("accesstype") : parentAuth.get("ACCESSTYPE");
            }else{
                atObj = rptAuthority.get("ACCESSTYPE") == null ? rptAuthority.get("accesstype") : rptAuthority.get("ACCESSTYPE");
            }
            
            if(cb instanceof ReportBean){
                //report授权
                if(atObj == null){
                    doDefaultSet(rrequest, new StringBuffer("报表").append(cb.getId()).append("的访问权限未设定！").toString(), cb);
                    return;
                }
                exeWbcsAuthority(rrequest,String.valueOf(atObj),cb);
                continue;
            }else if(cb instanceof AbsContainerConfigBean){
                //容器授权
                if(atObj == null){
                    doDefaultSet(rrequest, new StringBuffer("容器").append(cb.getId()).append("的访问权限未设定！").toString(), cb);
                    return;
                }
                //如果容器的授权状态是不可读状态和写状态，则终止向下层递归，继续同级组件循环
                if(AccessTypeConst.nonread.equals(atObj)||AccessTypeConst.write.equals(atObj)){
                    exeWbcsAuthority(rrequest, String.valueOf(atObj), cb);
                    continue;
                }
                exeWbcsAuthority(rrequest, String.valueOf(atObj), cb);
                doAuthority(rrequest, ((AbsContainerConfigBean)cb).getMChildren());
            }else{//非容器和report的情况不做任何处理
                continue;
            }
        }
    }
    
    void packetPageMenus(ReportRequest rrequest){
        //if(mpagemenus == null || mpagemenus.size() == 0)initPageMenus(rrequest.getConnection(), pageroleid[0], pageroleid[1]);
        //遍历mpagemenus 按照jcollection,json,xml等不同类型分别处理
        for(String locationtype : mpagemenus.keySet())
        {
            List<Map<String, Object>> lstmenus = mpagemenus.get(locationtype);
            Object packetypeObj = lstmenus.get(0).get("packetype");
            if(packetypeObj == null){
                log.error(new StringBuffer("在方位类型表中name值为").append(locationtype).append("的类型为设定数据封装类型").toString());
                continue;
            }
            switch(PageMenusPackagingConst.valueOf(String.valueOf(packetypeObj))){
                case jcollection:
                    rrequest.getRequest().setAttribute(locationtype,mpagemenus.get(locationtype));
                    break;
                case json:
                    ObjectMapper jsonMapper = JacksonMapper.getInstance();
                    StringWriter sw = new StringWriter();JsonGenerator gen;
                    try
                    {
                        gen=new JsonFactory().createJsonGenerator(sw);
                        jsonMapper.writeValue(gen, lstmenus); gen.close();
                    }catch(IOException e)
                    {
                        e.printStackTrace();
                    }
                    StringBuffer paramsBuf=new StringBuffer().append("{pageid:'"+rrequest.getPagebean().getId()+"'").append(",datas:"+sw.toString()+"}");
                    rrequest.getWResponse().addOnloadMethod(new StringBuffer("onload").append(locationtype).toString(),paramsBuf.toString(),false);
                    break;
                case xml: break;
                default: break;
            }
        }
    }
    
    /**
     * 默认情况下组件为不可读状态
     * @param rrequest
     * @param cids
     */
    void doDefaultSet(ReportRequest rrequest, String logmsg, IComponentConfigBean... cids){
        if(logmsg != null)log.warn(logmsg);
        innersetauth(rrequest, "display", "false", cids);
    }
    
    /**
     * WBCS插件统一赋权方法
     * @param rrequest
     * @param accesstype
     * @param cids
     */
    void exeWbcsAuthority(ReportRequest rrequest, String accesstype, IComponentConfigBean... cids){
        switch(AccessTypeConst.valueOf(accesstype)){
            case nonread:
                innersetauth(rrequest, "display", "false", cids);
                break;
            case readonly:
                //innersetauth(rrequest, "readonly", "true", cids);
                if(cids[0] instanceof ReportBean){
                    rrequest.setAttribute(cids[0].getId()+"_ACCESSMODE","readonly");
                    ButtonsBean bsb = cids[0].getButtonsBean();
                    if(bsb!=null){
                        List<AbsButtonType> lstButs = bsb.getAllDistinctButtonsList();
                        for(AbsButtonType btnitem:lstButs)
                        {
                            if(btnitem instanceof WbcsButton){
                               if(btnitem.getName().lastIndexOf("CstmBtn") > 0){
                                    rrequest.authorize(cids[0].getId(), com.wbcs.util.Consts.BUTTON_PART, btnitem.getName(), "display", "false");
                               }
                            }
                        }
                    }
                }
                break;
            case readwrite:
                if(cids[0] instanceof ReportBean){
                    ButtonsBean bsb = cids[0].getButtonsBean();
                    if(bsb!=null){
                        List<AbsButtonType> lstButs = bsb.getAllDistinctButtonsList();
                        for(AbsButtonType btnitem:lstButs)
                        {
                            if(btnitem instanceof WbcsButton){
                               Map<String, Object> buttonauth = mauthorities.get(btnitem.getName());
                               //if(buttonauth != null && (AccessTypeConst.readonly.equals(buttonauth.get("ACCESSTYPE"))||AccessTypeConst.write.equals(buttonauth.get("ACCESSTYPE"))))
                               if(
                                  buttonauth == null || buttonauth.get("ACCESSTYPE") == null
                                 )
                               {
                                   rrequest.authorize(cids[0].getId(), com.wbcs.util.Consts.BUTTON_PART, btnitem.getName(), "display", "false");
                               }
                            }
                        }
                    }
                }
                break;
            default:
                if(cids[0] instanceof ReportBean){
                    ButtonsBean bsb = cids[0].getButtonsBean();
                    //控制列权限
                    Iterator<String> it = mauthorities.keySet().iterator();
                    while(it.hasNext()){
                        Map<String, Object> authitem = mauthorities.get(it.next());
                        if(Consts.AUTH_COL.equals(authitem.get("TYPE"))&&authitem.get("ACCESSTYPE")==null){
                            rrequest.authorize(cids[0].getId(),com.wbcs.util.Consts.DATA_PART,String.valueOf(authitem.get("CODE")),"display","false");
                        }
                    }
                    
                    //控制按钮权限
                    if(bsb!=null){
                        List<AbsButtonType> lstButs = bsb.getAllDistinctButtonsList();
                        for(AbsButtonType btnitem:lstButs)
                        {
                            /*if(btnitem instanceof AbsEditableReportButton)
                            {
                                if(
                                     buttonauth == null || buttonauth.get("ACCESSTYPE") == null
                                  )
                                {
                                    rrequest.authorize(cids[0].getId(), com.wbcs.util.Consts.BUTTON_PART, "type{"+btnitem.getButtonType()+"}", "display", "false");
                                }
                            }
                            else */
                            if(btnitem instanceof WbcsButton)
                            { 
                                String btnname = btnitem.getName();
                                String prefix = "", subfix = "";
                                if(btnitem instanceof AbsEditableReportButton)
                                {
                                    btnname = btnitem.getButtonType();
                                    prefix = "type{"; subfix = "}";
                                }
                                    //if(buttonauth != null && (AccessTypeConst.readonly.toString().equals(buttonauth.get("ACCESSTYPE"))||AccessTypeConst.write.toString().equals(buttonauth.get("ACCESSTYPE"))))
                                Map<String, Object> buttonauth = mauthorities.get(btnname);
                                if(buttonauth == null || buttonauth.get("ACCESSTYPE") == null)
                                {
                                    rrequest.authorize(cids[0].getId(), com.wbcs.util.Consts.BUTTON_PART, prefix+btnname+subfix, "display", "false");
                                }
                           }
                        }
                    }
                }
                break;//write
        }
    }
    
    private void innersetauth(ReportRequest rrequest, String accesstype, String isok, IComponentConfigBean[] cids){
        if(cids.length == 1)
            rrequest.authorize(cids[0].getId(),null,null,accesstype,isok);
        else if(cids.length == 2)
            rrequest.authorize(cids[0].getId(),cids[1].getId(),null,accesstype,isok);
        else if(cids.length == 3)
            rrequest.authorize(cids[0].getId(),cids[1].getId(),cids[2].getId(),accesstype,isok);
    }
    
    private Map<String, Object> getValidComponent(IComponentConfigBean cb){
        Map<String, Object> authObj = mauthorities.get(cb.getParentContainer().getId());
        if(authObj == null){
            authObj = getValidComponent(cb.getParentContainer());
        }
        return authObj;
    }
    
    public enum AccessTypeConst{
        nonread, readonly, write, readwrite;
    }
    
    public enum PageMenusPackagingConst{
        jcollection, json, xml;
    }
}

